WebSocket 全局封装

MuYan2024-03-24VueVue
  • 该文档主要记录下 WebSocket 的基础封装配置,封装方法可以通用,部分配置基于 Vue3,可自行修改相应逻辑

配置文件

// webSocket.ts
import type { App } from 'vue';

// socket 自定义配置
interface configInfo {
  reconnect: number; // 重连次数
  reconnectAttempts: number; // 重连最大次数
  reconnectInterval: number; // 重连间隔(毫秒)
  reconnection: boolean; // 是否重连
  autoConnect: boolean; // 创建时是否自动连接
  heartbeatInterval: number; // 心跳间隔(毫秒)
}

const socketInfo: configInfo = {
  reconnect: 0,
  reconnectAttempts: 10,
  reconnectInterval: 1000,
  reconnection: true,
  autoConnect: false,
  heartbeatInterval: 2000
};

type CallbackFunction = (...args: any[]) => void;


// 回调data数据处理
export const dataTreating = (data: any) => {
  return new Promise((resolve, reject) => {
    resolve(data)
  });
};

class WebSocketPlugin {
  private url = '';

  private socket: any = null;

  private heartbeatIntervalId: any = null;

  private openCallbacks: (() => void)[] = [];
  private messageCallbacks: ((data: any) => void)[] = [];
  private closeCallbacks: (() => void)[] = [];
  private errorCallbacks: ((error: Event) => void)[] = [];

  constructor(url: string = '', data = {}) {
    if (!this.getToken()) {
      return;
    }
    const socketUrl = "ws://" + window.location.host + "/ws";
    this.url = url || socketUrl;
    this.socket = null;
    if (socketInfo.autoConnect) {
      this.connect();
    }
  }

  // 重连
  private reconnectSocket = () => {
    this.stopHeartbeat();
    if (!this.getToken()) {
      console.error('未登录,无法连接WebSocket');
      socketInfo.reconnectAttempts = 0;
      return;
    }
    if (this.isCloseSocket || this.connecting || this.isOpen()) {
      return;
    }
    // 不自动重连 or 超出请求次数
    if (!socketInfo.reconnection || socketInfo.reconnect >= socketInfo.reconnectAttempts) {
      return;
    }
    socketInfo.reconnectAttempts += 1;
    // 进行重连
    setTimeout(() => {
      this.connect();
    }, socketInfo.reconnectInterval);
  };

  // 登录凭证
  private getToken() {
    return '';
  }

  // 是否正在连接
  private connecting = false;

  // 初始化连接
  connect(cb?: CallbackFunction) {
    if (!this.getToken()) {
      console.error('未登录,无法连接WebSocket');
      return;
    }
    if (this.connecting) {
      return;
    }
    this.isCloseSocket = false;
    this.connecting = true;
    this.socket = new WebSocket(this.url);

    // 连接成功
    this.socket.onopen = () => {
      this.connecting = false;
      socketInfo.reconnect = 0;
      this.startHeartbeat();
      this.openCallbacks.forEach(callback => callback());
      cb && cb();
    };

    // 接收消息
    this.socket.onmessage = (event: any) => {
      this.messageCallbacks.forEach(callback => callback(event));
    };

    // 关闭服务
    this.socket.onclose = () => {
      this.connecting = false;
      this.reconnectSocket();
      this.closeCallbacks.forEach(callback => callback());
    };

    // 连接错误
    this.socket.onerror = (error: any) => {
      this.connecting = false;
      this.reconnectSocket();
      this.errorCallbacks.forEach(callback => callback(error));
    };
  }

  // 是否已连接
  isOpen() {
    return this.socket && this.socket.readyState === WebSocket.OPEN;
  }

  // 发送信息
  send(data: any) {
    return new Promise((resolve, reject) => {
      if (this.isOpen()) {
        this.socket.send(data);
        resolve(true);
      } else {
        this.reconnectSocket();
        reject(false);
      }
    });
  }

  // 定义心跳连接
  private startHeartbeat() {
    this.heartbeatIntervalId = setInterval(() => {
      this.send(`PING:{timestamp:${new Date().getTime()}}`);
    }, socketInfo.heartbeatInterval);
  }
  // 关闭心跳连接
  private stopHeartbeat() {
    clearInterval(this.heartbeatIntervalId);
  }

  // 手动关闭,不再重连
  private isCloseSocket = false;

  // 关闭 socket
  disconnect() {
    this.isCloseSocket = true;
    if (this.socket) {
      this.socket.close();
    }
  }

  // 连接成功回调
  onOpen(callback: () => void): void {
    this.openCallbacks.push(callback);
  }
  // 数据返回回调
  onMessage(callback: (data: any) => void): void {
    this.messageCallbacks.push(callback);
  }
  // 关闭回调
  onClose(callback: () => void): void {
    this.closeCallbacks.push(callback);
  }
  // 报错回调
  onError(callback: (error: Event) => void): void {
    this.errorCallbacks.push(callback);
  }
}

export { WebSocketPlugin };

export default {
  install: (app: App, obj?: configInfo) => {
    if (typeof WebSocket === 'undefined') {
      console.log('浏览器不支持WebSocket');
      return;
    }
    Object.assign(socketInfo, obj);
    const socket = new WebSocketPlugin();
    // 定义全局变量
    // app.config.globalProperties.$socket = socket;
    // 全局注入
    app.provide('socket', socket);
  }
};

使用

全局使用

// main.ts
import SocketIO from "./webSocket";
import App from "./App.vue";
const app = createApp(App);

app.use(SocketIO).mount("#app");
// 要调用的页面
import { dataTreating } from "./webSocket";
const socketObj: any = inject("socket");

// 回调信息处理
const messageCallback = (event: any) => {
  dataTreating(event).then((res: any) => {
    // 你的业务逻辑
  });
};

onMounted(() => {
  // 获取信息
  socketObj?.onMessage(messageCallback);
  // 以下内容为可选方法
  // 连接成功事件,只会在重连 or 第一次连接的时候执行
  socketObj?.onOpen(() => {});
  // 关闭连接事件
  socketObj?.onClose(() => {});
  // 连接错误事件
  socketObj?.onError(() => {});
});

// 发送
socketObj.send("自定义要发送的信息");

onBeforeUnmount(() => {
  // 关闭连接,可选
  socketObj?.disconnect();
});

自定义 hooks

// useSocket.ts
import { WebSocketPlugin, dataTreating } from "./webSocket";

export { dataTreating };
export const useSocket = (
  url: string,
  messageCallback: (data: any) => void,
  socketParams: {
    [key: string]: any,
  } = {},
  init?: () => void
) => {
  const socketObj = new WebSocketPlugin(url, socketParams);

  const initSuccess = ref(false);

  onMounted(async () => {
    socketObj?.onOpen(() => {
      if (!initSuccess.value) {
        initSuccess.value = true;
        init && init();
      }
    });
    socketObj?.onMessage((data: any) => {
      messageCallback && messageCallback(data);
    });
  });

  onBeforeUnmount(() => {
    // 关闭连接
    socketObj?.disconnect();
  });

  return {
    socketObj,
  };
};
  • 自定义 webSocket 局部使用
import { useSocket, dataTreating } from "./useSocket";

// 回调信息处理
const messageCallback = (event: any) => {
  dataTreating(event).then((res: any) => {
    // 你的业务逻辑
  });
};

const { socketObj } = useSocket(
  // url 连接地址
  "url",
  // socket回调
  messageCallback,
  // 连接socket时的传值(可选)
  socketParams,
  // 成功连接后要执行的方法(可选)
  () => {}
);

// 发送
socketObj.send("自定义要发送的信息");

// 以下内容为(可选方法)
// 连接成功事件,只会在重连 or 第一次连接的时候执行
socketObj?.onOpen("自定义方法");
// 关闭连接事件
socketObj?.onClose("自定义方法");
// 连接错误事件
socketObj?.onError("自定义方法");
上次更新 2026/6/23 11:49:15
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.8